
/*Copyright (C) 2011  Jens Schwarzhaupt

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

package de.dhbw.GpsTools;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.*;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**This class provides methods to handle *.gpx files.
 * At the moment (V_0.1.1) it's only possible to read *.gpx files and get the gps tracks
 * that are contained in the *.gpx files. 
 * 
 * @author Jens Schwarzhaupt
 *
 */
public class GpxHandler {
	
	private File file;
	private Element rootTag;
	private Document gpxDocument;
	
	/**Constructor
	 * 
	 * @param path The path of the *.gpx file that will be parsed.
	 */
	public GpxHandler(String path){
		file = new File(path);
	}
	
	public void readGpx()
	{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		try {
			builder = factory.newDocumentBuilder();
			gpxDocument = builder.parse(file);
			rootTag = gpxDocument.getDocumentElement();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void writeGpx() throws TransformerException
	{
	    TransformerFactory transFactory = TransformerFactory.newInstance();
	    Transformer trans = transFactory.newTransformer();
	    trans.setOutputProperty(OutputKeys.INDENT, "yes");
	    trans.setOutputProperty(OutputKeys.METHOD, "xml");
	    trans.transform(new DOMSource(gpxDocument), new StreamResult(file));
	}
	
	public void writeGpx(String path) throws TransformerException
	{
		file = new File(path);
	    writeGpx();
	}
	
	public void addTrack(AdvancedTrack track) throws ParserConfigurationException
	{
		if (gpxDocument == null)
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			gpxDocument = builder.newDocument();
			rootTag = gpxDocument.createElement("gpx");
			rootTag.setAttribute("xmlns", "http://www.topografix.com/GPX/1/1");
			rootTag.setAttribute("Version", "1.1");
			gpxDocument.appendChild(rootTag);
			
		}
		
		Element trkElement = gpxDocument.createElement("trk");
		Element trkSegElement = gpxDocument.createElement("trkseg");
		
		for (int i = 0; i < track.size(); i++)
		{
			addWaypointToTrack(trkSegElement, track.get(i));
		}
		
		trkElement.appendChild(trkSegElement);
		rootTag.appendChild(trkElement);
	}
	
	private void addWaypointToTrack(Element trkSegElement, Waypoint wpt)
	{
		SimpleDateFormat dateToString = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		StringBuilder dateFormat;
		DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.US);
		df.applyPattern("###.######");
		
		
		Element wptElement = gpxDocument.createElement("trkpt");
		Element time = gpxDocument.createElement("time");
		Element ele = gpxDocument.createElement("ele");
		
		wptElement.setAttribute("lat", df.format(wpt.getLatitude()));
		wptElement.setAttribute("lon", df.format(wpt.getLongitude()));
		
		ele.setTextContent(Double.toString(wpt.getElevation()));
		
		dateFormat = new StringBuilder(dateToString.format(wpt.getTime()));
		time.setTextContent(dateFormat.toString());
		
		wptElement.appendChild(ele);
		wptElement.appendChild(time);
		trkSegElement.appendChild(wptElement);
	}
	
	
	/**Get the number of tracks the *.gpx file has.
	 * 
	 * @return
	 */
	//@SuppressWarnings("finally")
	public int getNumberOfTracks(){
		NodeList children = rootTag.getElementsByTagName("trk");
		return children.getLength();
	}
	
	/**Get a list of all tracks the *.gpx file has.
	 * 
	 * @return A list of tracks as AdvancedTrack objects.
	 */
	public List<AdvancedTrack> getTracks(){
		NodeList gpxTracks = rootTag.getElementsByTagName("trk");
		List<AdvancedTrack> tracks = new ArrayList<AdvancedTrack>();
		
		for(int i = 0; i < gpxTracks.getLength(); i++){
			tracks.add(this.getTrackFromGpx(gpxTracks.item(i)));
		}
		return tracks;
	}
	
	private Waypoint getWaypointFromGpx(Node node){
		
		Waypoint wpt = new Waypoint();
		String fixAsString;
		NamedNodeMap coordinates;
		DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
		
		coordinates = node.getAttributes();
		wpt.setLatitude(Double.parseDouble(coordinates.getNamedItem("lat").getNodeValue()));
		wpt.setLongitude(Double.parseDouble(coordinates.getNamedItem("lon").getNodeValue()));
		
		NodeList children = node.getChildNodes();

		for (int i = 0; i < children.getLength(); i++){

			if (children.item(i).getNodeName().equals("time")){
				try {
					wpt.setTime(formatter.parse(children.item(i).getChildNodes().item(0).getNodeValue()));
					//System.out.println(children.item(i).getChildNodes().item(0).getNodeValue());
				} catch (DOMException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if (children.item(i).getNodeName().equals("ele")){
				//System.out.println("Hoehe: " + children.item(i).getChildNodes().item(0).getNodeValue() + "m");
				wpt.setElevation(Double.parseDouble(children.item(i).getChildNodes().item(0).getNodeValue()));
			}
			else if (children.item(i).getNodeName().equals("fix")){
				fixAsString = children.item(i).getChildNodes().item(0).getNodeValue();
				if (fixAsString.equals("3d")){
					wpt.setSatFixQuality(3);
				}
				else if (fixAsString.equals("2d")){
					wpt.setSatFixQuality(2);
				}
				else{
					wpt.setSatFixQuality(0);
				}
			}
			else if (children.item(i).getNodeName().equals("sat")){
				wpt.setNumberOfSats(Integer.parseInt(children.item(i).getChildNodes().item(0).getNodeValue()));
			}
		}
		
		return wpt;
	}
	
	private AdvancedTrack getTrackFromGpx(Node gpxTrack){
		AdvancedTrack tempTrack = new AdvancedTrack();
		NodeList allNodes = gpxTrack.getChildNodes();
		NodeList wpts;

		for (int i = 0; i < allNodes.getLength(); i++){
			if (allNodes.item(i).getNodeName().equals("trkseg")){
				wpts = allNodes.item(i).getChildNodes();
				for (int j = 0; j < wpts.getLength(); j++){
					if (wpts.item(j).getNodeName().equals("trkpt")){
						tempTrack.add(this.getWaypointFromGpx(wpts.item(j)));
					}
				}
			}
		}
		
		return tempTrack;
	}

}
